#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h>
#include <pthread.h> 
#include <time.h>
#include <signal.h>
#include <string.h>
#include <math.h>
#include <linux/unistd.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sched.h>
#include <signal.h>
#include <setjmp.h>
#include <errno.h>
#include <assert.h>


#include "SDL/SDL.h"



#define HEARTBEATS_PORT 36667
#define SERVER_PORT 36666
#define BUFLEN 512
#define GATE_NUM 28
#define T_MAX 20 
#define WIDTH 1024
#define HEIGHT 768
#define TT_SIGUSR1 (SIGUSR1)
#define TT_SIGUSR2 (SIGUSR2)
#define TIME_INTERVAL_1 1
#define TIME_INTERVAL_2 2

int count = 0;
int heartbeat_count = 0;
int g_seg_counter = 0;
static struct itimerval oldtv;
static struct itimerval oldtv_heartbeat;
char arg0[64];
char arg1[8];
char arg2[8];
char arg3[8];

int own_port = 0;
int semaph = 0;
int seg_num = 0;

struct seg_arguments
{
  unsigned int minval;	//min number of segments
  unsigned int maxval;	//max number of segments
  unsigned int thbeat;	//heartbeat time
}arg;

typedef struct node *link_seg;
typedef struct 
{
	char ip[BUFLEN];
	int  port;
	short alive;
} seginfo;

struct node {
    seginfo seg;
    link_seg next;
};

static link_seg head =NULL;



struct in_addr localInterface;
//struct sockaddr_in kill_recv_group;
struct sockaddr_in kill_last_groupSock;
struct sockaddr_in kill_all_groupSock;
struct sockaddr_in heartbeat_group_sock, s_addr_in;

pid_t srv_listen = -1;
int maxtime = 0;
int g_num_process = 0;
int g_kill_me = 0;
//pthread_mutex_t mut; 
char ip[BUFLEN];
static int noOfAliveThreads = 0;
pthread_t tid[T_MAX];
pthread_t g_thread;
pthread_t g_heartbeat_thread;
pthread_t g_display_thread;
pthread_t g_kill_recv_thread;

FILE *g_test;

//global variables for all the child processes
pid_t heartbeat_pid = -1 ;
pid_t seglogic_pid = -1 ;
pid_t display_pid = -1 ;
	
char kill_msg;

struct sockaddr_in group_sock;
char buf[BUFLEN]="0";
int socket_heartbeat=0;
FILE *fp = NULL;  //the log file
int seg_cnt = 0;
char seg[1024]="\0";
char logbuf[1024]="\0";

char gates_adr[GATE_NUM][20] = {	//to be replaced with real lab nodes addresses
	"tile-0-0.local", 
	"tile-0-1.local", 
	"tile-0-2.local", 
	"tile-0-3.local",
	"tile-1-0.local", 
	"tile-1-1.local", 
	"tile-1-2.local",
	"tile-1-3.local", 
	"tile-2-0.local", 
	"tile-2-1.local", 
	"tile-2-2.local",
	"tile-2-3.local", 
	"tile-3-0.local", 
	"tile-3-1.local",
	"tile-3-2.local",
	"tile-3-3.local", 
	"tile-4-0.local", 
	"tile-4-1.local", 
	"tile-4-2.local",
	"tile-4-3.local", 
	"tile-5-0.local", 
	"tile-5-1.local",
	"tile-5-2.local", 
	"tile-5-3.local",
	"tile-6-0.local", 
	"tile-6-1.local", 
	"tile-6-2.local", 
	"tile-6-3.local"	};

link_seg make_node (seginfo seg)
{
	link_seg p = malloc(sizeof(struct node));
	strcpy(p->seg.ip, seg.ip);
	p->seg.port = seg.port;
	p->seg.alive = 0;
	p->next = NULL;
	return p;
}
void append_node(link_seg head, link_seg p)
{
	if(NULL == head)
	{
		p->next = NULL;
	}
	else
	{
		p->next = head->next;
	}
	head->next = p;
	
}
void free_node(link_seg p)
{
	free(p);
}
link_seg search(seginfo seg)
{
	link_seg p;
	for(p=head;p;p=p->next)
	{
		if((strcmp(seg.ip, p->seg.ip) == 0) && (p->seg.port == seg.port))
		{
			p->seg.alive = 1;
			return p;
		}
	}
	return NULL;
}
void insert(link_seg p)
{
	p->next = head;
	head = p;
}
void delete_from_list(link_seg p)
{
	link_seg pre;
	if(p == head)
	{
		head = p->next;
		return;
	}
	for(pre=head; pre; pre = pre->next)
	{
		if(pre->next == p )
		{
			pre->next = p->next;
			return;
		}
	}
}

void traverse(void (*visit)(link_seg))
{
	link_seg p;
		
	for(p=head;p;p=p->next)
	{
		visit(p);
	}
	return;
}
void destroy(void)
{
	link_seg q,p=head;
	while(p)
	{
		q=p;
		p=p->next;
		free_node(q);
	}
	return;
}
void push(link_seg p)
{
	insert(p);
}

link_seg pop(void)
{
	if(NULL == head)
	{
		return NULL;
	}
	else
	{
		link_seg p=head;
		head = head->next;
		return p;
	}
	return;
}

timer_t create_timer(int signo) 
{
    timer_t timerid;
    struct sigevent se;
    se.sigev_notify=SIGEV_SIGNAL;
    se.sigev_signo = signo;
    if (timer_create(CLOCK_REALTIME, &se, &timerid) == -1) 
    {
        perror("Failed to create timer");
        exit(-1);
    }
    return timerid;
}

void set_timer(timer_t timerid, int seconds) 
{
    struct itimerspec timervals;
    timervals.it_value.tv_sec = seconds;
    timervals.it_value.tv_nsec = 0;
    timervals.it_interval.tv_sec = seconds;
    timervals.it_interval.tv_nsec = 0;

    if (timer_settime(timerid, 0, &timervals, NULL) == -1) 
    {
        perror("Failed to start timer");
        exit(-1);
    }
    return;
}

void install_sighandler(int signo, void(*handler)(int)) 
{
    sigset_t set;
    struct sigaction act;

    /* Setup the handler */
    act.sa_handler = handler;
    act.sa_flags = SA_RESTART;
    sigaction(signo, &act, 0);

    /* Unblock the signal */
    sigemptyset(&set);
    sigaddset(&set, signo);
    sigprocmask(SIG_UNBLOCK, &set, NULL);

    return;
}





void signal_handler(int m)
{
	int i;
	loginfo("handler");
	
	if (count == maxtime) 
	{
		//cancel the active threads
		
		pthread_cancel(g_thread);
	}
	else 
	{
		count++;
		
		sprintf(logbuf, "signal_handler count is %d, maxtime is %d", count, maxtime);
		loginfo(logbuf);
	}
	
}
int get_app_name(char *p, char *pname)
{
	char *q, *name;
	int len = 0, lenp = 0;
	q = p;
	len = strlen(q);
	lenp = strlen(p);
	q = q + len -1;
	
	while( *q != '/' )
	{
		q--;
		if (strlen(q) == strlen(p))
		{
			strcpy(pname, q);
			return 0;
		}
	}
	strcpy(pname, q+1);
	return 0;
}
int loginfo(char *pstr)
{
	time_t lt;
	char *ptm ;
	char buf[1024];
	int i, cnt= 0;
	struct tm *p;
	lt = time(NULL);
	p = localtime(&lt);
	ptm = ctime(&lt);
	
	char hostname[1024]="";

	gethostname(hostname, 1024);

	sprintf(buf, "[%s]%d-%d-%d %d:%d:%d %s\n", hostname, 1900+p->tm_year, 1 + p->tm_mon, p->tm_mday,  p->tm_hour, p->tm_min, p->tm_sec, pstr);
	
	for ( i = 0; buf[i] != '\0'; i++ ) 
	{
		cnt++;
	}
	fwrite(buf,cnt, 1, fp);	
	i = fflush(fp);
	return 0;
}

int hostname_to_ip(char * hostname , char* ip)
{
    struct hostent *he;
    struct in_addr **addr_list;
    int i;

    if ( (he = gethostbyname( hostname ) ) == NULL)
    {
        // get the host info
        loginfo("gethostbyname");
        return -1;
    }
    addr_list = (struct in_addr **) he->h_addr_list;
    for(i = 0; addr_list[i] != NULL; i++)
    {
        //Return the first one;
        strcpy(ip , inet_ntoa(*addr_list[i]) );
        return 0;
    }
    return -1;
}


int spread_the_worm(int gate_num)
{

        struct sockaddr_in servaddr, client_addr;
        int sockfd_spread, source_fd;
        char hostname[1024], iobuffer[1024];
        struct hostent *phost = NULL;
	char filename[1024], segment_path[1024];
	int j,num, ret, size = 0;
	char pname[BUFLEN];
	char *pprogram;
	char path[1024], arg[1024];
	int errno;
   
	sprintf(logbuf, "Sending file to gate nr %i: %s, port 36600\n", gate_num, gates_adr[gate_num]);
	loginfo(logbuf);

        sockfd_spread = socket( AF_INET, SOCK_STREAM, 0);
        if ( sockfd_spread < 0)
        {
                loginfo("socket err in spread");
                return 1;
        }
	
	bzero(&client_addr, sizeof(client_addr));
	client_addr.sin_family = AF_INET;
	client_addr.sin_addr.s_addr = htons(INADDR_ANY);
	client_addr.sin_port = htons(0);

        servaddr.sin_family = AF_INET;

        servaddr.sin_port = htons(36600);

	phost = gethostbyname(gates_adr[gate_num]);

        
	sprintf(logbuf,"gate addr = %s", inet_ntoa( *(struct in_addr*)phost->h_addr_list[0]));
	loginfo(logbuf);
        servaddr.sin_addr = (*(struct in_addr*)phost->h_addr_list[0]);
	sprintf(logbuf,"sin_family = %d, sin_port = %d, sin_addr = %s, \n", servaddr.sin_family, servaddr.sin_port, inet_ntoa(servaddr.sin_addr));
	loginfo(logbuf);
        if ( connect( sockfd_spread, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
        {
                sprintf(logbuf, "Connect err: %d\n", errno);
		loginfo(logbuf);
                
		return -1;
        }
	
	//send the three arguments
	sprintf(arg, "%s,%s,%s", arg1, arg2, arg3);
	sprintf(logbuf, "arg list si %s, %s, %s",arg1, arg2, arg3);	
	loginfo(logbuf);
	ret = write(sockfd_spread, arg, sizeof(arg) );
        if ( -1 == ret)
        {
                sprintf(logbuf,"write err %d\n", errno);
                loginfo(logbuf);
                return -1;
        }



	ret = get_app_name(arg0, pname);
	if ( ret != 0)
	{
		loginfo("get_app_name failed");
		return -1;
	}

	sprintf(filename, "/tmp/wzh000/%s",  pname);
	sprintf(logbuf,"filename is %s", filename);
	loginfo(logbuf);
	
	if ((source_fd = open(filename, O_RDONLY, 0) ) == -1)
        {
                loginfo("file open err\n");
                return -1;
        }
   

	while ( (num = read(source_fd, iobuffer, sizeof(iobuffer))) > 0)
        {
                write(sockfd_spread, iobuffer, num);
		//sprintf(logbuf, "num is %d", num);
		//loginfo(logbuf);
		size += num;
        }
	sprintf(logbuf,"sent data: %6d bytes\n", size);
	loginfo(logbuf);
	close(source_fd);
        close(sockfd_spread);
	loginfo("send file success");
	
	
	return 0;
}

void apply_surface ( int x, int y, SDL_Surface* source, SDL_Surface* destination )
{
 
     // make a temporary rectangle to hold the offsets
     SDL_Rect offset;
 
     // give the offsets to the rectangle
     offset.x = x;
     offset.y = y;
 
     // blit the surface
     SDL_BlitSurface( source, NULL, destination, &offset );
}

int display_new()
{
	
	//The images 
	SDL_Surface* hello = NULL; 
	SDL_Surface* screen = NULL;
	SDL_Surface* star = NULL; 
	SDL_Surface* tstar = NULL;
	SDL_Surface* hstar = NULL;
	
	int i,j=0;
	int k, l;
	int x, y;
	int h, t;
	char dbuf[512];
	int stars_count = 0;
	srand(time(0));
	//Start SDL 
	SDL_Init( SDL_INIT_EVERYTHING ); 
	//Set up screen 
	k = rand()%624;
	l = rand()%568;
	sprintf(dbuf, "SDL_VIDEO_WINDOW_POS=%d,%d", k, l);
	SDL_putenv(dbuf);
	
	
	
	screen = SDL_SetVideoMode( 400, 200, 24, SDL_SWSURFACE ); 
	
	
	//Load image 
	hello = SDL_LoadBMP( "/home/inf3200/students/wzh000/ok/logo.bmp" );
	star = SDL_LoadBMP( "/home/inf3200/students/wzh000/ok/star.bmp" );
	tstar = SDL_LoadBMP( "/home/inf3200/students/wzh000/ok/tstar.bmp" );
	hstar = SDL_LoadBMP( "/home/inf3200/students/wzh000/ok/hstar.bmp" );
	
	//Apply image to screen 
	SDL_BlitSurface( hello, NULL, screen, NULL ); 
	
	//max 6400 segments shown - if hundreds only; or 4699 if all
	
	
	while(1)
	{	
	
		stars_count = seg_num; //seg_stars is a global value taken from the main		
		
		
		//stars_count = how many segments we have
		h = stars_count / 100;
		t = (stars_count-100*h) / 10;
		i = stars_count - (100*h +10*t);
		
		x=0; y=0;
		
		for (h; h>0; h--)
		{
			apply_surface( 160+x, 0+y, hstar, screen );
			x+=30;
		
			if (x==240)
			{
				x=0;
				y+=25;
			}
		}
		
		
		
		for (t; t>0; t--)
		{
			apply_surface( 160+x, 0+y, tstar, screen );
			x+=30;
		
			if (x==240)
			{
				x=0; 
				y+=25;
			}
		}
		
		
		
		for (i; i>0; i--)
		{
		
			apply_surface( 160+x, 0+y, star, screen );
			x+=30;
		
			if (x==240)
			{
				x=0; 
				y+=25;
			}
			 
		}
		
		
		
		//Update Screen 
		SDL_Flip( screen ); 
		//Pause 
		SDL_Delay( 1000 );
	
	
	}
	//Free the loaded image 
	SDL_FreeSurface( hello ); 
	//Quit SDL 
	
	SDL_Quit(); 
	return 0; 
	
}

void *display(void) 
{
	SDL_Surface *screen, *logo;
	int i = 0, j;
	char dbuf[512];
	SDL_Rect logoRect;
	atexit(SDL_Quit);

	if( SDL_Init(SDL_INIT_VIDEO) < 0 ) 
	{
		exit(1);
	}
	logo = SDL_LoadBMP("/home/inf3200/students/wzh000/sdllogo.bmp");
	if ( logo == NULL)
	{
		exit(1);
	}
	SDL_ShowCursor(SDL_DISABLE);
	
	i = rand()%WIDTH;
	j = rand()%HEIGHT;
		
	sprintf(dbuf, "SDL_VIDEO_WINDOW_POS=%d,%d", i, j);
	SDL_putenv(dbuf);	
	screen = SDL_SetVideoMode(123,67,16,SDL_HWSURFACE|SDL_DOUBLEBUF);	
		
	SDL_BlitSurface(logo , NULL , screen , &logoRect );
	SDL_Flip(screen);
	while(1)
	{
	}

}

int kill_me()
{
	//killing all own processes and itself on demand

	exit(0); 		
	return 0;	//i don't see much sense in this line, but it was requested

	//maybe we also should add some cleaning functions before
}


int kill_all()
{
	//sending kill_all signal to everyone else by broadcast to port 366666
	char kbuf[512];
	struct in_addr localInterface;
	struct sockaddr_in si_other;
  	int  s_kill_all, i, slen=sizeof(si_other);
  	int ilen = 0;
	int len, ret;
	char hostname[BUFLEN], ip[BUFLEN];

  	if ((s_kill_all=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
    	loginfo("socket");
	
	memset((char *) &kill_all_groupSock, 0, sizeof(kill_all_groupSock));
	kill_all_groupSock.sin_family = AF_INET;
	kill_all_groupSock.sin_addr.s_addr = inet_addr("226.1.1.1");
	kill_all_groupSock.sin_port = htons(36666);
	
	gethostname(hostname, BUFLEN);
	if( -1 == (ret = hostname_to_ip(hostname, ip)) )
	{
		loginfo("hostname_to_ip in send_heartbeat");
		return 'N';
	}	
	
    localInterface.s_addr = inet_addr(ip);
	if(setsockopt(s_kill_all, IPPROTO_IP, IP_MULTICAST_IF, (char *)&localInterface, sizeof(localInterface)) < 0)
	{
		loginfo("Setting local interface error");
		return 1;
	}	

	sprintf(kbuf, "ALL");	
        len = sizeof( struct sockaddr);
        if (sendto(s_kill_all, kbuf, 512, 0, (struct sockaddr*)&kill_all_groupSock, sizeof(kill_all_groupSock) )==-1)
		{
			loginfo("sendto() in kill_all");
			return 1;
		}
	
	kill_me();
	return 0;

}

void *kill_receive(void)
{

	//receiving kill signal (A/ALL - all, S/SLF - self)
	struct sockaddr_in si_other;
	struct sockaddr_in si_me;
	struct ip_mreq kill_recv_group;
	char hostname[BUFLEN], ip[BUFLEN];
	int ret;
	int s_recv;
	int slen = sizeof(struct sockaddr);
	int errno;

	char kill_tab[512];

	if ((s_recv=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)	
	{
		sprintf(logbuf,"socket in kill_receive err, errno = %d", errno);
		loginfo(logbuf);
		return ;
	}
	sprintf(logbuf, "s_recv is %d", s_recv);
	loginfo(logbuf);
	int reuse = 1;
	if(setsockopt(s_recv, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0)
	{
		sprintf(logbuf,"Setting SO_REUSEADDR error. errno = %d", errno);
		loginfo(logbuf);
		close(s_recv);
		return ;
	}

	memset((char *) &si_me, 0, sizeof(si_me));
	
	si_me.sin_family = AF_INET;
	si_me.sin_port = htons(SERVER_PORT);
	si_me.sin_addr.s_addr = htonl(INADDR_ANY);
	if (bind(s_recv, (struct sockaddr *)&si_me, sizeof(si_me))==-1)
	{
	  sprintf(logbuf,"bind err in kill_receive, errno = %d", errno);
	  loginfo(logbuf);
	  close(s_recv);
	  return ;
	}
	kill_recv_group.imr_multiaddr.s_addr = inet_addr("226.1.1.1");
	gethostname(hostname, BUFLEN);
	if( -1 == (ret = hostname_to_ip(hostname, ip)) )
	{
		loginfo("hostname_to_ip in send_heartbeat");
		return ;
	}	
	
    kill_recv_group.imr_interface.s_addr = inet_addr(ip);
    if(setsockopt(s_recv, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&kill_recv_group, sizeof(kill_recv_group)) < 0)
    {
        loginfo("Adding multicast group error");
        close(s_recv);
        return ;
    }
	loginfo("before recvfrom in kill_receive");
		while (recvfrom(s_recv, kill_tab, 512, 0, (struct sockaddr *)&si_other, &slen)!=-1) 
		{
			sprintf(logbuf,"kill_received packet from %s:%d\nData: %s\n\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port), kill_tab);
	        	loginfo(logbuf);

	        	if (kill_tab[0] == 'A' && kill_tab[1] == 'L' && kill_tab[2] == 'L') kill_msg = 'A';
	        	else if  (atoi(kill_tab)==own_port) kill_msg = 'S';
			else return ;

	        	//in message we should receive: char 'A' if killing all, 'S' if killing self
	       		 switch (kill_msg)
	        	{
	                case 'A':               //handling kill_all
	                        kill_me();
	                        break;


	                case 'S':       //handling kill_me
	                        kill_me();
	                        break;

	                default: break;
	        	}
			close(s_recv);

		}		
		loginfo("recvfrom err in kill_receive");
		

}


void signal_handler_heartbeat(int m)
{
	int len, ilen;
	heartbeat_count ++;
	len = sizeof( struct sockaddr);
	ilen = sizeof( s_addr_in);
	if (sendto(socket_heartbeat, buf, BUFLEN, 0, (struct sockaddr*)&heartbeat_group_sock, sizeof(heartbeat_group_sock) )==-1)
	{
		loginfo("sendto() in signal_handler_heartbeat");
		
	}
	getsockname(socket_heartbeat,(struct sockaddr *)&s_addr_in,&ilen);
	own_port = ntohs(s_addr_in.sin_port);
	sprintf(logbuf, "heartbeat port %d", own_port);
	loginfo(logbuf);
}

void *send_heartbeat(void) 
{
	struct in_addr localInterface;

	int ret;
	char  ip[BUFLEN], hostname[BUFLEN];
	char loopch = 1;

	timer_t timer1 = create_timer(TT_SIGUSR1);	
	if ((socket_heartbeat=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
    	loginfo("socket err in send_heartbeat");
	
	memset((char *) &heartbeat_group_sock, 0, sizeof(heartbeat_group_sock));
	heartbeat_group_sock.sin_family = AF_INET;
	heartbeat_group_sock.sin_addr.s_addr = inet_addr("227.1.1.1");
	heartbeat_group_sock.sin_port = htons(36667);

	gethostname(hostname, BUFLEN);
	if( -1 == (ret = hostname_to_ip(hostname, ip)) )
	{
		loginfo("hostname_to_ip in send_heartbeat");
		return ;
	}
	
	localInterface.s_addr = inet_addr(ip);
	
	if(setsockopt(socket_heartbeat, IPPROTO_IP, IP_MULTICAST_IF, (char *)&localInterface, sizeof(localInterface)) < 0)
	{
		loginfo("Setting local interface error");
		return ;
	}	
	install_sighandler(TT_SIGUSR1, signal_handler_heartbeat);
	set_timer(timer1, TIME_INTERVAL_1);
	while(1)
	{
	}

	close(socket_heartbeat);

}

void *listen_thread(void) 
{
	int s,ret,len, i, errno;
	struct sockaddr_in si_me, si_other;
	struct ip_mreq recv_group;
	char mbuf[512];
	char cport[5];
	char hostname[BUFLEN], ip[BUFLEN];
	seginfo seg;
	link_seg tmp;
	len = sizeof(struct sockaddr);
	if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)	
	{
		sprintf(logbuf,"socket err, errno %d", errno);
		loginfo(logbuf);
	}

	//REUSE THINGS	
	int reuse = 1;
	if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0)
	{
		sprintf(logbuf,"Setting SO_REUSEADDR error. errno = %d", errno);
		loginfo(logbuf);
		close(s);
	}

	memset((char *) &si_me, 0, sizeof(si_me));
	si_me.sin_family = AF_INET;
	si_me.sin_port = htons(HEARTBEATS_PORT);
	si_me.sin_addr.s_addr = htonl(INADDR_ANY);
	if (bind(s, (struct sockaddr *)&si_me, sizeof(si_me))==-1) 
	{
		loginfo("bind");
		close(s);
	}
	recv_group.imr_multiaddr.s_addr = inet_addr("227.1.1.1");
	gethostname(hostname, BUFLEN);
        if( -1 == (ret = hostname_to_ip(hostname, ip)) )
        {
                loginfo("hostname_to_ip in send_heartbeat");
        }

	recv_group.imr_interface.s_addr = inet_addr(ip);
	sprintf(logbuf, "ip in listen thread is %s", ip);
	loginfo(logbuf);

	if(setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&recv_group, sizeof(recv_group)) < 0)
	{
		sprintf(logbuf,"Adding multicast group error, errno = %d\n", errno);
		loginfo(logbuf);
		close(s);
	}
	loginfo("before recvfrom");	
	if ((recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *)&si_other, &len))==-1)
	{
		loginfo("recv err\n");
		return ;
	}
	sprintf(logbuf,"Received packet from %s:%d, Data: %s\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port), buf);
	loginfo(logbuf);
	seg.port = ntohs(si_other.sin_port);
	strcpy(seg.ip, inet_ntoa(si_other.sin_addr));
	//search this segment in the list.if not found, add it to the list
	tmp = search(seg);
	loginfo("after search\n");
	if (NULL == tmp)
	{	
		loginfo("not found, insert\n");
		link_seg p_seg = make_node(seg);
		p_seg->next = head;
		insert(p_seg);
		sprintf(logbuf,"data is %s:%d\n", head->seg.ip, head->seg.port);
		loginfo(logbuf);
	}
	else
	{
		loginfo("seg exist\n");
	}
	close(s);

}

void init_sigaction(void)
{
	struct sigaction tact;

	tact.sa_handler = signal_handler;
	tact.sa_flags = 0;

	sigemptyset(&tact.sa_mask);
	sigaction(SIGALRM, &tact, NULL);
}
int main(int argc, char *argv[])
{

	unsigned int seg_counter = 0;
	unsigned int gate_num;


	int i, errno; 
	int segment_already_stored = 0;
	int ret,zw=0;
	struct sockaddr_in si_me, si_other;
	int s, slen=sizeof(si_other);

	char tmp[BUFLEN];
	char pname[BUFLEN];
	char hostname[BUFLEN], ip[BUFLEN];
	pid_t main_pid;
	int sqrtcount;
	int num_thread;
	FILE *fpzhangwei;
 
	gethostname(hostname, BUFLEN);
	if( -1 == (ret = hostname_to_ip(hostname, ip)) )
	{
		fpzhangwei = fopen("/tmp/wzh000/host_2_ip", "a+");
	        fclose(fpzhangwei);

		exit (0);
	}	
	if ( argc != 4 )
	{
		fpzhangwei = fopen("/tmp/wzh000/argc", "a+");
	        fclose(fpzhangwei);

		exit(0);
	}

	arg.minval = atoi(argv[1]);
	arg.maxval = atoi(argv[2]);
	arg.thbeat = atoi(argv[3]);
	strcpy(arg0, argv[0]);
	strcpy(arg1, argv[1]);
	strcpy(arg2, argv[2]);
	strcpy(arg3, argv[3]);
	if (arg.minval > arg.maxval)
	{
		fpzhangwei = fopen("/tmp/wzh000/minval", "a+");
        	fclose(fpzhangwei);
		exit(0);
	}
	

	srand (time(NULL));
	maxtime = arg.thbeat*10;
	ret = get_app_name(argv[0], pname);
	
	if ( ret != 0)
	{
		fpzhangwei = fopen("/tmp/wzh000/get_app_name", "a+");
        	fclose(fpzhangwei);

		exit (EXIT_FAILURE);
	}

	sprintf(hostname, "/tmp/wzh000/%s.log", pname);
	remove(hostname);
	fp = fopen( hostname, "a+");
	if ( NULL == fp)
	{
		exit (EXIT_FAILURE);
	}  
	sprintf(logbuf, "arglist is %s, %s, %s, %s", arg0, arg1, arg2, arg3);
	loginfo(logbuf);

	//create a thread to send heartbeat
	pthread_create(&g_heartbeat_thread,NULL,(void*)send_heartbeat,NULL);
	
	pthread_create(&g_display_thread,NULL,(void*)display,NULL);
	pthread_create(&g_kill_recv_thread,NULL,(void*)kill_receive,NULL);
	
	link_seg p;
	int target = 0;
	int p_rand;
	double pp_rand, probability = 0.0, factor = 0.0;
	timer_t timer2 = create_timer(TT_SIGUSR2);
	install_sighandler(TT_SIGUSR2, signal_handler);
	set_timer(timer2, TIME_INTERVAL_1);
	while(1)
	{
		
		while(count < 10 * arg.thbeat) 
		{

			////////////////////////////////////////
			//waiting for incoming heartbeats (server connection)
			////////////////////////////////////////

			sprintf(logbuf, "count is %d, seg_num is %d", count, seg_num);
			loginfo(logbuf);

			pthread_create(&g_thread,NULL,(void*)listen_thread,NULL);
			pthread_join(g_thread, 0);
			//wait for the thread to exit			
		}
		
		for (p = head; p; p = p->next)
		{
			if (p->seg.alive == 0)
			{
				loginfo("delete a seg");
				delete_from_list(p);
				free_node(p);
			}	
		}
		//print list
		seg_num = 0;
		for ( p = head; p; p = p->next)
		{
			seg_num++;
			p->seg.alive = 0;
			sprintf(logbuf," ip is %s, port is %d, g_seg_num is %d\n", p->seg.ip, p->seg.port, seg_num);
			loginfo(logbuf);
		}
		
		sprintf(logbuf, "times up. total num is %d", seg_num);
		loginfo(logbuf);
		
		loginfo("Starting the kill/create block:");
		
		sqrtcount = 0; 
		target = (arg.minval + arg.maxval) / 2;
		p_rand = rand();
		pp_rand = (double)(p_rand % 100);
		probability = pp_rand / 100.00;
		sprintf(logbuf, "current seg num is %d, the target is %d, p_rand is %d, probability is %f", seg_num, target, p_rand, probability);
		loginfo(logbuf);
		//creating new segments block 
		if( seg_num < target)
		{
			
			if ((seg_num) <= target-(seg_num))
			{
				sprintf(logbuf, "because it'e better %d segments all spread to reach %d, all_spread", seg_num, target);
				loginfo(logbuf);
				gate_num = rand() % GATE_NUM;							
				spread_the_worm(gate_num);
			}
			else 
			{
				factor = (double)((double)(target-(seg_num))/(double)(seg_num));
				sprintf(logbuf, "probability is %f, factor is %f", probability, factor);
				loginfo(logbuf);
				if(probability > factor)
				{
				sprintf(logbuf, "I got the probability %f greater than %f, spread", probability, factor);
				loginfo(logbuf);
				gate_num = rand() % GATE_NUM;
				
				spread_the_worm(gate_num);
				}
			}
		}
		if( seg_num >= target)
		{
			factor = (double)((double)((seg_num) - target)/(double)(seg_num));						
			if (probability > factor)
			{
				sprintf(logbuf, "I got the probability %f greater than %f, kill_me", probability, factor);
				loginfo(logbuf);
				kill_me();
			}
		}
		
		count = 0;
		
	}
			  
		
	return 0;

}
